

#include <algorithm>
#include <cstddef>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

std::vector<std::string> words(const std::string& text)
{
    std::ifstream in(text);
    std::istream_iterator<std::string> start(in), end;
    return std::accumulate(start, end, std::vector<std::string> {},
        [](std::vector<std::string>&& acc, std::string word) {
            if (!word.empty()) {
                acc.emplace_back(std::move(word));
            }
            return std::move(acc);
        });
}

template <typename C, typename P = std::decay<typename C::value_type>::type>
std::unordered_map<P, size_t> count_occurrences(const C& words)
{
    std::unordered_map<P, size_t> words_map;
    return std::accumulate(begin(words), end(words), std::unordered_map<P, size_t> {},
        [](std::unordered_map<P, size_t>&& words_map, const P& word) {
            if (words_map.count(word)) {
                words_map[word]++;
            } else {
                words_map.insert({ word, 1 });
            }
            return std::move(words_map);
        });
}

template <typename C,
    typename P1 = std::remove_cv<typename C::value_type::first_type>::type,
    typename P2 = typename C::value_type::second_type>
std::vector<std::pair<P2, P1>> reverse_pairs(const C& words_map)
{
    std::vector<std::pair<P2, P1>> results(words_map.size());
    std::transform(std::begin(words_map), std::end(words_map), std::begin(results), [](const std::pair<const P1, P2>& w) {
        return std::make_pair(w.second, w.first);
    });
    return results;
}

template <typename C>
auto sort_by_frequency(C words_pair)
{
    std::sort(begin(words_pair), end(words_pair), std::greater<>());
    return words_pair;
}

void print_pairs(const std::vector<std::pair<size_t, std::string>>& words_pair)
{
    std::for_each(words_pair.cbegin(), words_pair.cend(), [](const std::pair<size_t, std::string>& pair) {
        std::cout << pair.first << " : " << pair.second << std::endl;
    });
}

int main()
{
    std::string file_name = "/data/workspace/Func_progra/src/fp_4_3.cc";
    print_pairs(sort_by_frequency(reverse_pairs(count_occurrences(words(file_name)))));
}